// SPDX-License-Identifier: GPL-2.0-or-later

/***************************************************************************
 ***************************************************************************/

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include "imp.h"
#include <helper/binarybuffer.h>
#include <target/algorithm.h>
#include <target/cortex_m.h>

/* timeout values */
#define FLASH_ERASE_TIMEOUT 10000
#define FLASH_WRITE_TIMEOUT 10

#define G32R5_OTP_BANK_BASE 0x0810A000
#define G32R5_FLASH_BANK_BASE 0x08000000

#define FLASH_R_BASE 0x50010000
#define FAPI_BASE_ADDRESS (0x50010000U)
#define SECTOR_SIZE (8 * 1024)
#define BANKWIDTHINBYTES 0x10
#define FLASH_FMSTAT 0x54U
#define FLASH_FRDCNTL 0x0U
#define FLASH_FRDCNTL_RWAIT_S ((unsigned int)8)
#define FLASH_FRDCNTL_RWAIT_M ((unsigned int)0xFF)
// Rwait
#define RWAIT_1 ((unsigned int)1)
#define RWAIT_2 ((unsigned int)2)
#define RWAIT_3 ((unsigned int)3)
#define RWAIT_4 ((unsigned int)4)
#define RWAIT_5 ((unsigned int)5)
#define RWAIT_6 ((unsigned int)6)
#define SMW_VALID_KEY 0x0810BB00
#define SMW_TIMER_OPTION_OPT 0x0810BB04
#define SMW_SETTING_OPTION_L_OPT 0x0810BB08
#define SMW_SETTING_OPTION_H_OPT 0x0810BB0C
#define SMW_SETTING_OPTION_2_OPT 0x0810BB10
#define SMW_SETTING_OPTION_3_OPT 0x0810BB14
#define SMW_SMP_WHV_OPTION_L_OPT 0x0810BB18
#define SMW_SMP_WHV_OPTION_H_OPT 0x0810BB1C
#define SMW_SME_WHV_OPTION_L_OPT 0x0810BB20
#define SMW_SME_WHV_OPTION_H_OPT 0x0810BB24
#define TWAKEUP_OPT 0x0810BB28
#define CHECKSUM_OPT 0x0810BB2C
#define SMW_VALID_KEY_VAL ((unsigned int)0x20230810)
#define CHECKSUM_OPT 0x0810BB2C
#define SMW_TIMER_OPTION 0x444U
#define SMW_SETTING_OPTION_L 0x448U
#define SMW_SETTING_OPTION_H 0x44CU
#define SMW_SETTING_OPTION_2 0x450U
#define SMW_SETTING_OPTION_3 0x454U
#define SMW_SMP_WHV_OPTION_L 0x458U
#define SMW_SMP_WHV_OPTION_H 0x45CU
#define SMW_SME_WHV_OPTION_L 0x460U
#define SMW_SME_WHV_OPTION_H 0x464U
#define TWAKEUP 0x474U
#define SYSCLK_250M ((unsigned int)250)
#define FLASH_FMSTAT_NOT_WRITABLE ((unsigned int)0x00002000)
#define FLASH_CR 0x478U
#define FLASH_FAR 0x4C0U
#define FLASH_PROG_CFG 0x4C4U
#define FLASH_DIN0 0x500U
#define FLASH_DIN1 0x504U
#define FLASH_DIN2 0x508U
#define FLASH_DIN3 0x50CU
#define FLASH_DIN4 0x510U
#define FLASH_DIN5 0x514U
#define FLASH_DIN6 0x518U
#define FLASH_DIN7 0x51CU
#define FLASH_DIN8 0x520U
#define FLASH_FLASH_OP 0x5CCU
#define FLASH_ECC_ENABLE 0x600U
#define FLASH_CR_NBANK ((unsigned int)0x00000001)
#define FLASH_FRDCNTL 0x0U
#define FLASH_FMSTAT 0x54U
#define FLASH_FRD_INTF_CTRL 0x300U
#define FLASH_SMW_CR 0x440U
#define FLASH_SMW_CR_SMW_OP_ERASE ((unsigned int)0x00000003)
#define FLASH_SMW_CR_SMW_OP_PROGRAM ((unsigned int)0x00000004)
#define FLASH_SMW_CR_SMW_ARRAY_MAIN ((unsigned int)0x00000000)
#define FLASH_SMW_CR_SMW_ARRAY_INFO ((unsigned int)0x00000010)
#define FLASH_SMW_CR_SMW_ARRAY ((unsigned int)0x00000070)
#define FLASH_SMW_CR_SMW_HEM_100K ((unsigned int)0x00001000)
#define FLASH_FLASH_OP_0 ((unsigned int)0x00000001)
#define FLASH_FLASH_OP_1 ((unsigned int)0x00000008)
#define SINGLEMODE_BANK0_STARTADR ((unsigned int)0x08000000)
#define SINGLEMODE_BANK0_ENDADR ((unsigned int)0x0803ffff)
#define SINGLEMODE_BANK1_STARTADR ((unsigned int)0x08040000)
#define SINGLEMODE_BANK1_ENDADR ((unsigned int)0x0809ffff)
#define DUALMODE_BANK0_STARTADR ((unsigned int)0x08000000)
#define DUALMODE_BANK0_ENDADR ((unsigned int)0x0801FFFF)
#define DUALMODE_BANK1_STARTADR ((unsigned int)0x08020000)
#define DUALMODE_BANK1_ENDADR ((unsigned int)0x0809FFFF)
#define FLASH_FMSTAT_SUSPEND ((unsigned int)0x55555555)
#define FLASH_FMSTAT_CLEARSTATUS ((unsigned int)0x5A5A5A5A)
#define FLASH_FMSTAT_CLEARMORE ((unsigned int)0xA5A5A5A5)
#define FLASH_FMSTAT_PROGRAMRESUME ((unsigned int)0xBBBBBBBB)
#define FLASH_FMSTAT_ERASERESUME ((unsigned int)0xAAAAAAAA)

#define SCB ((SCB_Type *)((0xE000E000UL) + 0x0D00UL))
#define __SCB_DCACHE_LINE_SIZE 32U
#define SCB_CCR_IC_Pos 17U
#define SCB_CCR_IC_Msk (1UL << SCB_CCR_IC_Pos)             /*!< SCB CCR: IC Mask */
#define SCB_CCR_DC_Pos 16U
#define SCB_CCR_DC_Msk (1UL << SCB_CCR_DC_Pos)             /*!< SCB CCR: DC Mask */
#define SCB_DCISW_WAY_Pos 30U                              /*!< SCB DCISW: Way Position */
#define SCB_DCISW_WAY_Msk (3UL << SCB_DCISW_WAY_Pos)       /*!< SCB DCISW: Way Mask */
#define SCB_DCISW_SET_Pos 5U                               /*!< SCB DCISW: Set Position */
#define SCB_DCISW_SET_Msk (0xFFUL << SCB_DCISW_SET_Pos)    /*!< SCB DCISW: Set Mask */
#define SCB_DCISW_WAY_Pos 30U                              /*!< SCB DCISW: Way Position */
#define SCB_DCISW_WAY_Msk (3UL << SCB_DCISW_WAY_Pos)       /*!< SCB DCISW: Way Mask */
#define SCB_DCCISW_WAY_Pos 30U                             /*!< SCB DCCISW: Way Position */
#define SCB_DCCISW_WAY_Msk (3UL << SCB_DCCISW_WAY_Pos)     /*!< SCB DCCISW: Way Mask */
#define SCB_DCCISW_SET_Pos 5U                              /*!< SCB DCCISW: Set Position */
#define SCB_DCCISW_SET_Msk (0x1FFUL << SCB_DCCISW_SET_Pos) /*!< SCB DCCISW: Set Mask */
#define FLASH_FRD_INTF_CTRL_ART0_PRFTEN ((unsigned int)0x00000001)
#define FLASH_FRD_INTF_CTRL_ART0_ICEN ((unsigned int)0x00000002)
#define FLASH_FRD_INTF_CTRL_ART0_DCEN ((unsigned int)0x00000004)
#define FLASH_FRD_INTF_CTRL_ART0_ICRST ((unsigned int)0x00000008)
#define FLASH_FRD_INTF_CTRL_ART0_DCRST ((unsigned int)0x00000010)
#define FLASH_FRD_INTF_CTRL_ART1_PRFTEN ((unsigned int)0x00000020)
#define FLASH_FRD_INTF_CTRL_ART1_ICEN ((unsigned int)0x00000040)
#define FLASH_FRD_INTF_CTRL_ART1_DCEN ((unsigned int)0x00000080)
#define FLASH_FRD_INTF_CTRL_ART1_ICRST ((unsigned int)0x00000100)
#define FLASH_FRD_INTF_CTRL_ART1_DCRST ((unsigned int)0x00000200)
#define FLASH_FRD_INTF_CTRL_BUS_PRFTEN ((unsigned int)0x00000400)
#define FLASH_FRD_INTF_CTRL_BUS_BUF_CLR ((unsigned int)0x00000800)

#define FLASH_FMSTAT_NOT_WRITABLE ((unsigned int)0x00002000)
#define FLASH_FMSTAT_PGV ((unsigned int)0x00001000)
#define FLASH_FMSTAT_EV ((unsigned int)0x00000400)
#define FLASH_FMSTAT_BUSY ((unsigned int)0x00000100)
#define FLASH_FMSTAT_ERS ((unsigned int)0x00000080)
#define FLASH_FMSTAT_PGM ((unsigned int)0x00000040)
#define FLASH_FMSTAT_INVDAT ((unsigned int)0x00000020)
#define FLASH_FMSTAT_CSTAT ((unsigned int)0x00000010)
#define FLASH_FMSTAT_ESUSP ((unsigned int)0x00000004)
#define FLASH_FMSTAT_PSUSP ((unsigned int)0x00000002)

#define ECC_STARTADR ((unsigned int)0x09000000)
#define ECC_MAINFLASH_ENDADR ((unsigned int)0x09013FFF)
#define ECC_INFO_STARTADR ((unsigned int)0x09020000)
#define ECC_ENDADR ((unsigned int)0x090217ff)

#define FLASH_ECC_ENABLE_ENCODE_M ((unsigned int)0x000000F0)
#define FLASH_ECC_ENABLE_DECODE_M ((unsigned int)0x0000000F)
#define ECC_ENCODE_ENABLE 0xA0

#define PROG_DATA_ECC_BANK0 ((unsigned int)0x0000003F)
#define PROG_DATA_ECC_BANK1 ((unsigned int)0x00000FC0)

#define HALFBANKWIDTHINBYTES 0x8

#define PROG_DATA_BANK0_0 ((unsigned int)0x00000001)
#define PROG_DATA_BANK0_4 ((unsigned int)0x00000002)
#define PROG_DATA_BANK0_8 ((unsigned int)0x00000004)
#define PROG_DATA_BANK0_C ((unsigned int)0x00000008)
#define PROG_ECC_BANK0_L ((unsigned int)0x00000010)
#define PROG_ECC_BANK0_H ((unsigned int)0x00000020)

#define PROG_DATA_BANK1_0 ((unsigned int)0x00000040)
#define PROG_DATA_BANK1_4 ((unsigned int)0x00000080)
#define PROG_DATA_BANK1_8 ((unsigned int)0x00000100)
#define PROG_DATA_BANK1_C ((unsigned int)0x00000200)
#define PROG_ECC_BANK1_L ((unsigned int)0x00000400)
#define PROG_ECC_BANK1_H ((unsigned int)0x00000800)

#define DCSM0_STARTADR ((unsigned int)0x0810A000)
#define DCSM0_ENDADR ((unsigned int)0x0810A7FF)
#define DCSM1_STARTADR ((unsigned int)0x0810B000)
#define DCSM1_ENDADR ((unsigned int)0x0810B7FF)

#define SCB_CCSIDR_ASSOCIATIVITY_Pos 3U
#define SCB_CCSIDR_ASSOCIATIVITY_Msk (0x3FFUL << SCB_CCSIDR_ASSOCIATIVITY_Pos)
#define SCB_CCSIDR_NUMSETS_Pos 13U
#define SCB_CCSIDR_NUMSETS_Msk (0x7FFFUL << SCB_CCSIDR_NUMSETS_Pos)

#define CCSIDR_WAYS(x) (((x) & SCB_CCSIDR_ASSOCIATIVITY_Msk) >> SCB_CCSIDR_ASSOCIATIVITY_Pos)
#define CCSIDR_SETS(x) (((x) & SCB_CCSIDR_NUMSETS_Msk) >> SCB_CCSIDR_NUMSETS_Pos)
#define __ALIGNED(x) __attribute__((aligned(x)))
#define HWREG(x) (*((volatile unsigned int *)((uintptr_t)(x))))
#define EALLOW                              \
// __asm volatile("dsb 0xF" ::: "memory"); \
        // __arm_mcr(1, 0, 1, 0, 12, 0)
#define EDIS                                \
// __asm volatile("dsb 0xF" ::: "memory"); \
// __arm_mcr(1, 0, 0, 0, 12, 0)

typedef enum
{
    Fapi_FlashBank0,
    Fapi_FlashBank1
} Fapi_FlashBankType;

typedef enum
{
    Fapi_ProgramData = 0x0002,
    Fapi_EraseSector = 0x0006,
    Fapi_EraseBank = 0x0008,
    Fapi_ValidateSector = 0x000E,
    Fapi_ClearStatus = 0x0010,
    Fapi_ProgramResume = 0x0014,
    Fapi_EraseResume = 0x0016,
    Fapi_ClearMore = 0x0018
} Fapi_FlashStateCommandsType;

typedef enum
{
    Fapi_AutoEccGeneration, /* This is the default mode for the command and will auto generate the ecc for the provided data buffer */
    Fapi_DataOnly,          /* Command will only process the data buffer */
    Fapi_EccOnly,           /* Command will only process the ecc buffer */
    Fapi_DataAndEcc         /* Command will process data and ecc buffers */
} Fapi_FlashProgrammingCommandsType;

typedef enum
{
    Fapi_Status_Success = 0,        /* Function completed successfully */
    Fapi_Status_FsmBusy,            /* FSM is Busy */
    Fapi_Status_FsmReady,           /* FSM is Ready */
    Fapi_Status_AsyncBusy,          /* Async function operation is Busy */
    Fapi_Status_AsyncComplete,      /* Async function operation is Complete */
    Fapi_Error_Fail = 500,          /* Generic Function Fail code */
    Fapi_Error_StateMachineTimeout, /* State machine polling never returned ready and timed out */
    Fapi_Error_OtpChecksumMismatch, /* Returned if OTP checksum does not match expected value */
    Fapi_Error_InvalidDelayValue,   /* Returned if the Calculated RWAIT value exceeds 15  - Legacy Error */
    Fapi_Error_InvalidHclkValue,    /* Returned if FClk is above max FClk value - FClk is a calculated from HClk and RWAIT/EWAIT */
    Fapi_Error_InvalidCpu,          /* Returned if the specified Cpu does not exist */
    Fapi_Error_InvalidBank,         /* Returned if the specified bank does not exist */
    Fapi_Error_InvalidAddress,      /* Returned if the specified Address does not exist in Flash or OTP */
    Fapi_Error_InvalidReadMode,     /* Returned if the specified read mode does not exist */
    Fapi_Error_AsyncIncorrectDataBufferLength,
    Fapi_Error_AsyncIncorrectEccBufferLength,
    Fapi_Error_AsyncDataEccBufferLengthMismatch,
    Fapi_Error_FeatureNotAvailable, /* FMC feature is not available on this device */
    Fapi_Error_FlashRegsNotWritable /* Returned if Flash registers are not writable due to security */
} Fapi_StatusType;

typedef struct
{
    unsigned int au32StatusWord[4];
} Fapi_FlashStatusWordType;

#define     __IM     volatile const
#define     __IOM    volatile
#define     __OM     volatile

/**
  \brief  Structure type to access the System Control Block (SCB).
 */
typedef struct
{
    __IM unsigned int cpuid;       /*!< Offset: 0x000 (R/ )  CPUID Base Register */
    __IOM unsigned int ICSR;       /*!< Offset: 0x004 (R/W)  Interrupt Control and State Register */
    __IOM unsigned int VTOR;       /*!< Offset: 0x008 (R/W)  Vector Table Offset Register */
    __IOM unsigned int AIRCR;      /*!< Offset: 0x00C (R/W)  Application Interrupt and Reset Control Register */
    __IOM unsigned int SCR;        /*!< Offset: 0x010 (R/W)  System Control Register */
    __IOM unsigned int CCR;        /*!< Offset: 0x014 (R/W)  Configuration Control Register */
    __IOM uint8_t SHPR[12U];   /*!< Offset: 0x018 (R/W)  System Handlers Priority Registers (4-7, 8-11, 12-15) */
    __IOM unsigned int SHCSR;      /*!< Offset: 0x024 (R/W)  System Handler Control and State Register */
    __IOM unsigned int CFSR;       /*!< Offset: 0x028 (R/W)  Configurable Fault Status Register */
    __IOM unsigned int HFSR;       /*!< Offset: 0x02C (R/W)  HardFault Status Register */
    __IOM unsigned int DFSR;       /*!< Offset: 0x030 (R/W)  Debug Fault Status Register */
    __IOM unsigned int MMFAR;      /*!< Offset: 0x034 (R/W)  MemManage Fault Address Register */
    __IOM unsigned int BFAR;       /*!< Offset: 0x038 (R/W)  BusFault Address Register */
    __IOM unsigned int AFSR;       /*!< Offset: 0x03C (R/W)  Auxiliary Fault Status Register */
    __IM unsigned int ID_PFR[2U];  /*!< Offset: 0x040 (R/ )  Processor Feature Register */
    __IM unsigned int ID_DFR;      /*!< Offset: 0x048 (R/ )  Debug Feature Register */
    __IM unsigned int ID_AFR;      /*!< Offset: 0x04C (R/ )  Auxiliary Feature Register */
    __IM unsigned int ID_MMFR[4U]; /*!< Offset: 0x050 (R/ )  Memory Model Feature Register */
    __IM unsigned int ID_ISAR[6U]; /*!< Offset: 0x060 (R/ )  Instruction Set Attributes Register */
    __IM unsigned int CLIDR;       /*!< Offset: 0x078 (R/ )  Cache Level ID register */
    __IM unsigned int CTR;         /*!< Offset: 0x07C (R/ )  Cache Type register */
    __IM unsigned int CCSIDR;      /*!< Offset: 0x080 (R/ )  Cache Size ID Register */
    __IOM unsigned int CSSELR;     /*!< Offset: 0x084 (R/W)  Cache Size Selection Register */
    __IOM unsigned int CPACR;      /*!< Offset: 0x088 (R/W)  Coprocessor Access Control Register */
    __IOM unsigned int NSACR;      /*!< Offset: 0x08C (R/W)  Non-Secure Access Control Register */
    unsigned int RESERVED0[21U];
    __IOM unsigned int SFSR; /*!< Offset: 0x0E4 (R/W)  Secure Fault Status Register */
    __IOM unsigned int SFAR; /*!< Offset: 0x0E8 (R/W)  Secure Fault Address Register */
    unsigned int RESERVED1[69U];
    __OM unsigned int STIR;  /*!< Offset: 0x200 ( /W)  Software Triggered Interrupt Register */
    __IOM unsigned int RFSR; /*!< Offset: 0x204 (R/W)  RAS Fault Status Register */
    unsigned int RESERVED2[14U];
    __IM unsigned int MVFR0; /*!< Offset: 0x240 (R/ )  Media and VFP Feature Register 0 */
    __IM unsigned int MVFR1; /*!< Offset: 0x244 (R/ )  Media and VFP Feature Register 1 */
    __IM unsigned int MVFR2; /*!< Offset: 0x248 (R/ )  Media and VFP Feature Register 2 */
    unsigned int RESERVED3[1U];
    __OM unsigned int ICIALLU; /*!< Offset: 0x250 ( /W)  I-Cache Invalidate All to PoU */
    unsigned int RESERVED4[1U];
    __OM unsigned int ICIMVAU;  /*!< Offset: 0x258 ( /W)  I-Cache Invalidate by MVA to PoU */
    __OM unsigned int DCIMVAC;  /*!< Offset: 0x25C ( /W)  D-Cache Invalidate by MVA to PoC */
    __OM unsigned int DCISW;    /*!< Offset: 0x260 ( /W)  D-Cache Invalidate by Set-way */
    __OM unsigned int DCCMVAU;  /*!< Offset: 0x264 ( /W)  D-Cache Clean by MVA to PoU */
    __OM unsigned int DCCMVAC;  /*!< Offset: 0x268 ( /W)  D-Cache Clean by MVA to PoC */
    __OM unsigned int DCCSW;    /*!< Offset: 0x26C ( /W)  D-Cache Clean by Set-way */
    __OM unsigned int DCCIMVAC; /*!< Offset: 0x270 ( /W)  D-Cache Clean and Invalidate by MVA to PoC */
    __OM unsigned int DCCISW;   /*!< Offset: 0x274 ( /W)  D-Cache Clean and Invalidate by Set-way */
    __OM unsigned int BPIALL;   /*!< Offset: 0x278 ( /W)  Branch Predictor Invalidate All */
} SCB_Type;

struct g32x_options
{
    uint8_t rdp;
    uint8_t user;
    unsigned short data;
    unsigned int protection;
};

struct g32x_flash_bank
{
    struct g32x_options option_bytes;
    int ppage_size;
    bool probed;
    unsigned int user_bank_size;
};

unsigned int SYSCLK = 0;

/**
 * @brief  Calculates the Fletcher checksum from the given address and length.
 * @param  pu16Data:Address to start calculating the checksum from.
 * @param  u16Length:Number of 16-bit words to use in calculation.
 * @retval 32-bit Fletcher Checksum value.
 */
unsigned int Fapi_calculateFletcherChecksum(
    unsigned short const *pu16Data,
    unsigned short u16Length)
{
    unsigned int u32Sum1 = 0xFFFFU & 0xFFFFFFFF, u32Sum2 = 0xFFFFU & (0xFFFFFFFF >> 16U);
    unsigned short u16Index, idx;
    unsigned short data;

    for (u16Index = 0U; u16Index < u16Length; u16Index++)
    {
        idx = u16Index ^ 1; // little endian mode otherwise use u16Index only
        data = pu16Data[idx];
        u32Sum1 += data;
        u32Sum1 = (u32Sum1 >> 16U) + (u32Sum1 & 0xFFFFU);
        u32Sum2 += u32Sum1;
        u32Sum2 = (u32Sum2 >> 16U) + (u32Sum2 & 0xFFFFU);
    }
    return ((u32Sum2 << 16U) | u32Sum1);
}

/**
  * @brief  Initializes the FMC for erase and program operations.
  * @param  oFlashBank:Fapi_FlashBank0 or Fapi_FlashBank01.
  * @retval FLASH Fapi_StatusType: The returned value can be: Fapi_Error_InvalidBank,
            Fapi_Error_InvalidHclkValue, Fapi_Error_OtpChecksumMismatch or Fapi_Status_Success.
  */
Fapi_StatusType Fapi_setActiveFlashBank(Fapi_FlashBankType oFlashBank)
{
    unsigned int Checksum, CycleClk;
    // InvalidBank
    if ((oFlashBank != Fapi_FlashBank0) && (oFlashBank != Fapi_FlashBank1))
        return Fapi_Error_InvalidBank;

    CycleClk = HWREG(0x0810B934);

    if (SYSCLK <= CycleClk)
    {
        ;
    }
    else if ((SYSCLK <= (CycleClk * 2)) && (SYSCLK > (CycleClk * 1)))
    {
        if (((HWREG(FLASH_R_BASE + FLASH_FRDCNTL) >> FLASH_FRDCNTL_RWAIT_S) & FLASH_FRDCNTL_RWAIT_M) < RWAIT_1)
            return Fapi_Error_InvalidHclkValue;
    }
    else if ((SYSCLK <= (CycleClk * 3)) && (SYSCLK > (CycleClk * 2)))
    {
        if (((HWREG(FLASH_R_BASE + FLASH_FRDCNTL) >> FLASH_FRDCNTL_RWAIT_S) & FLASH_FRDCNTL_RWAIT_M) < RWAIT_2)
            return Fapi_Error_InvalidHclkValue;
    }
    else if ((SYSCLK <= (CycleClk * 4)) && (SYSCLK > (CycleClk * 3)))
    {
        if (((HWREG(FLASH_R_BASE + FLASH_FRDCNTL) >> FLASH_FRDCNTL_RWAIT_S) & FLASH_FRDCNTL_RWAIT_M) < RWAIT_3)
            return Fapi_Error_InvalidHclkValue;
    }
    else if ((SYSCLK <= (CycleClk * 5)) && (SYSCLK > (CycleClk * 4)))
    {
        if (((HWREG(FLASH_R_BASE + FLASH_FRDCNTL) >> FLASH_FRDCNTL_RWAIT_S) & FLASH_FRDCNTL_RWAIT_M) < RWAIT_4)
            return Fapi_Error_InvalidHclkValue;
    }
    else if ((SYSCLK <= (CycleClk * 6)) && (SYSCLK > (CycleClk * 5)))
    {
        if (((HWREG(FLASH_R_BASE + FLASH_FRDCNTL) >> FLASH_FRDCNTL_RWAIT_S) & FLASH_FRDCNTL_RWAIT_M) < RWAIT_5)
            return Fapi_Error_InvalidHclkValue;
    }
    else if ((SYSCLK <= SYSCLK_250M) && (SYSCLK > (CycleClk * 6)))
    {
        if (((HWREG(FLASH_R_BASE + FLASH_FRDCNTL) >> FLASH_FRDCNTL_RWAIT_S) & FLASH_FRDCNTL_RWAIT_M) < RWAIT_6)
            return Fapi_Error_InvalidHclkValue;
    }

    if (HWREG(SMW_VALID_KEY) == SMW_VALID_KEY_VAL)
    {
        // OTP Check
        Checksum = Fapi_calculateFletcherChecksum((unsigned short *)0x0810BB00, 0x16);
        if (Checksum != HWREG(CHECKSUM_OPT))
            return Fapi_Error_OtpChecksumMismatch;
        // Write flash pram
        EALLOW;
        HWREG(FLASH_R_BASE + SMW_TIMER_OPTION) = (HWREG(SMW_TIMER_OPTION_OPT) & 0x1FFF);
        HWREG(FLASH_R_BASE + SMW_SETTING_OPTION_L) = (HWREG(SMW_SETTING_OPTION_L_OPT) & 0x7FFFFFFF);
        HWREG(FLASH_R_BASE + SMW_SETTING_OPTION_H) = (HWREG(SMW_SETTING_OPTION_H_OPT) & 0x0FFFFFFF);
        HWREG(FLASH_R_BASE + SMW_SETTING_OPTION_2) = (HWREG(SMW_SETTING_OPTION_2_OPT) & 0x1FFFFFFF);
        HWREG(FLASH_R_BASE + SMW_SETTING_OPTION_3) = (HWREG(SMW_SETTING_OPTION_3_OPT) & 0x0001FFFF);
        HWREG(FLASH_R_BASE + SMW_SMP_WHV_OPTION_L) = HWREG(SMW_SMP_WHV_OPTION_L_OPT);
        HWREG(FLASH_R_BASE + SMW_SMP_WHV_OPTION_H) = HWREG(SMW_SMP_WHV_OPTION_H_OPT);
        HWREG(FLASH_R_BASE + SMW_SME_WHV_OPTION_L) = HWREG(SMW_SME_WHV_OPTION_L_OPT);
        HWREG(FLASH_R_BASE + SMW_SME_WHV_OPTION_H) = HWREG(SMW_SME_WHV_OPTION_H_OPT);
        HWREG(FLASH_R_BASE + TWAKEUP) = (HWREG(TWAKEUP_OPT) & 0x1FF);
        EDIS;
    }
    return Fapi_Status_Success;
}

/**
 * @brief  Returns the status of the Flash State Machine.
 * @param  None.
 * @retval Fapi_Status_FsmBusy or Fapi_Status_FsmReady.
 */
Fapi_StatusType Fapi_checkFsmForReady(void)
{
    HWREG(FLASH_R_BASE + FLASH_FMSTAT);

    if ((HWREG(FLASH_R_BASE + FLASH_FMSTAT) & FLASH_FMSTAT_BUSY) == FLASH_FMSTAT_BUSY)
        return Fapi_Status_FsmBusy;
    else
        return Fapi_Status_FsmReady;
}

/**
 * @brief  Issues a command to the Flash State Machine.
 * @param  oCommand:can be one of the Fapi_ClearStatus, Fapi_ClearMore, Fapi_ProgramResume or Fapi_EraseResume.
 * @retval Fapi_Status_Success.
 */
Fapi_StatusType Fapi_issueAsyncCommand(Fapi_FlashStateCommandsType oCommand)
{
    EALLOW;
    // First clear status
    HWREG(FLASH_R_BASE + FLASH_FMSTAT) = FLASH_FMSTAT_CLEARSTATUS;
    // Wait busy
    while (Fapi_checkFsmForReady() == Fapi_Status_FsmBusy)
        ;

    if (oCommand == Fapi_ClearStatus)
    {
        HWREG(FLASH_R_BASE + FLASH_FMSTAT) = FLASH_FMSTAT_CLEARSTATUS;
    }
    else if (oCommand == Fapi_ClearMore)
    {
        HWREG(FLASH_R_BASE + FLASH_FMSTAT) = FLASH_FMSTAT_CLEARMORE;
    }
    else if (oCommand == Fapi_ProgramResume)
    {
        HWREG(FLASH_R_BASE + FLASH_FMSTAT) = FLASH_FMSTAT_PROGRAMRESUME;
    }
    else if (oCommand == Fapi_EraseResume)
    {
        HWREG(FLASH_R_BASE + FLASH_FMSTAT) = FLASH_FMSTAT_ERASERESUME;
    }
    FLASH_ClearCache_Buff();
    EDIS;
    return Fapi_Status_Success;
}

/**
  * @brief  Erases a specified sector in Main FLASH(All Bank Mode).
  * @param  oCommand: Command to issue to the FSM. Use Fapi_EraseSector.
  * @param  Sector_Address: The sector address in program memory to be erased.
  * @retval FLASH Fapi_StatusType: The returned value can be: Fapi_Error_FlashRegsNotWritable,
            Fapi_Error_FeatureNotAvailable, Fapi_Error_InvalidAddress, or Fapi_Status_Success.
  */
Fapi_StatusType Fapi_issueAsyncCommandWithAddress(
    Fapi_FlashStateCommandsType oCommand,
    unsigned int *pu32StartAddress)
{
    unsigned int Sector_Address = (unsigned int)pu32StartAddress;
    unsigned int UserEndadr;

    if ((HWREG(FLASH_R_BASE + FLASH_FMSTAT) & FLASH_FMSTAT_NOT_WRITABLE) != 0)
        return Fapi_Error_FlashRegsNotWritable;
    if (oCommand != Fapi_EraseSector)
        return Fapi_Error_FeatureNotAvailable;

    // Tcm addr
    if ((Sector_Address >= 0x00100000) && (Sector_Address <= 0x0019FFFF))
    {
        Sector_Address = Sector_Address - 0x00100000 + 0x08000000;
    }

    // Clear ERROR flag
    Fapi_issueAsyncCommand(Fapi_ClearStatus);
    // Wait busy
    while (Fapi_checkFsmForReady() == Fapi_Status_FsmBusy)
        ;

    // User Accessable FLASH
    UserEndadr = HWREG(0x0810AFF0);
    if (UserEndadr > 0x080A0000)
        UserEndadr = 0x080A0000;

    EALLOW;
    // Single Bank Mode
    if (HWREG(FLASH_R_BASE + FLASH_CR) & FLASH_CR_NBANK)
    {
        if ((Sector_Address >= SINGLEMODE_BANK0_STARTADR) && (Sector_Address <= SINGLEMODE_BANK0_ENDADR))
        {
            HWREG(FLASH_R_BASE + FLASH_SMW_CR) = 0;
            HWREG(FLASH_R_BASE + FLASH_SMW_CR) |= (FLASH_SMW_CR_SMW_OP_ERASE | FLASH_SMW_CR_SMW_HEM_100K);
            HWREG(FLASH_R_BASE + FLASH_FAR) = Sector_Address;
            HWREG(FLASH_R_BASE + FLASH_FLASH_OP) = (FLASH_FLASH_OP_0 | FLASH_FLASH_OP_1);
        }
        else if ((Sector_Address >= SINGLEMODE_BANK1_STARTADR) && (Sector_Address < UserEndadr))
        {
            HWREG(FLASH_R_BASE + FLASH_SMW_CR) = 0;
            HWREG(FLASH_R_BASE + FLASH_SMW_CR) |= (FLASH_SMW_CR_SMW_OP_ERASE | FLASH_SMW_CR_SMW_HEM_100K);
            HWREG(FLASH_R_BASE + FLASH_FAR) = Sector_Address;
            HWREG(FLASH_R_BASE + FLASH_FLASH_OP) = (FLASH_FLASH_OP_1);
        }
        else
        {
            EDIS;
            return Fapi_Error_InvalidAddress;
        }
    }
    else
    {
        // Dual Bank Mode
        // Bank0
        if ((Sector_Address >= DUALMODE_BANK0_STARTADR) && (Sector_Address <= DUALMODE_BANK0_ENDADR))
        {
            HWREG(FLASH_R_BASE + FLASH_SMW_CR) = 0;
            HWREG(FLASH_R_BASE + FLASH_SMW_CR) |= (FLASH_SMW_CR_SMW_OP_ERASE | FLASH_SMW_CR_SMW_HEM_100K);
            HWREG(FLASH_R_BASE + FLASH_FAR) = Sector_Address;
            HWREG(FLASH_R_BASE + FLASH_FLASH_OP) = (FLASH_FLASH_OP_0);
        }
        // Bank1
        else if ((Sector_Address >= DUALMODE_BANK1_STARTADR) && (Sector_Address < UserEndadr))
        {
            HWREG(FLASH_R_BASE + FLASH_SMW_CR) = 0;
            HWREG(FLASH_R_BASE + FLASH_SMW_CR) |= (FLASH_SMW_CR_SMW_OP_ERASE | FLASH_SMW_CR_SMW_HEM_100K);
            HWREG(FLASH_R_BASE + FLASH_FAR) = Sector_Address;
            HWREG(FLASH_R_BASE + FLASH_FLASH_OP) = (FLASH_FLASH_OP_1);
        }
        else
        {
            EDIS;
            return Fapi_Error_InvalidAddress;
        }
    }
    FLASH_ClearCache_Buff();
    EDIS;
    return Fapi_Status_Success;
}

#define __ICACHE_PRESENT 1
#define __DCACHE_PRESENT 1

/**
 * @brief  Clear buffer/cache.
 * @param  None.
 * @retval None.
 */
void FLASH_ClearCache_Buff(void)
{
    if (SCB->CCR & (unsigned int)SCB_CCR_IC_Msk) /*I-Cache */
    {
#if defined(__ICACHE_PRESENT) && (__ICACHE_PRESENT == 1U)
        // __asm volatile("dsb 0xF" ::: "memory");
        // __asm volatile("isb 0xF" ::: "memory");
        SCB->CCR &= ~(unsigned int)SCB_CCR_IC_Msk; /* disable I-Cache */
        SCB->ICIALLU = 0UL;                    /* invalidate I-Cache */
        // __asm volatile("dsb 0xF" ::: "memory");
        // __asm volatile("isb 0xF" ::: "memory");
        if (SCB->CCR & SCB_CCR_IC_Msk)
            return; /* return if ICache is already enabled */

        // __asm volatile("dsb 0xF" ::: "memory");
        // __asm volatile("isb 0xF" ::: "memory");
        SCB->ICIALLU = 0UL; /* invalidate I-Cache */
        // __asm volatile("dsb 0xF" ::: "memory");
        // __asm volatile("isb 0xF" ::: "memory");
        SCB->CCR |= (unsigned int)SCB_CCR_IC_Msk; /* enable I-Cache */
        // __asm volatile("dsb 0xF" ::: "memory");
        // __asm volatile("isb 0xF" ::: "memory");
#endif
    }
    if (SCB->CCR & (unsigned int)SCB_CCR_DC_Msk) /*D-Cache */
    {
#if defined(__DCACHE_PRESENT) && (__DCACHE_PRESENT == 1U)
        struct
        {
            unsigned int ccsidr;
            unsigned int sets;
            unsigned int ways;
        } locals
#if ((defined(__GNUC__) || defined(__clang__)) && !defined(__OPTIMIZE__))
            __ALIGNED(__SCB_DCACHE_LINE_SIZE)
#endif
                ;

        SCB->CSSELR = 0U; /* select Level 1 data cache */
        // __asm volatile("dsb 0xF" ::: "memory");

        SCB->CCR &= ~(unsigned int)SCB_CCR_DC_Msk; /* disable D-Cache */
        // __asm volatile("dsb 0xF" ::: "memory");

#if !defined(__OPTIMIZE__)
        /*
         * For the endless loop issue with no optimization builds.
         * More details, see https://github.com/ARM-software/CMSIS_5/issues/620
         *
         * The issue only happens when local variables are in stack. If
         * local variables are saved in general purpose register, then the function
         * is OK.
         *
         * When local variables are in stack, after disabling the cache, flush the
         * local variables cache line for data consistency.
         */
        /* Clean and invalidate the local variable cache. */
#if defined(__ICCARM__)
        /* As we can't align the stack to the cache line size, invalidate each of the variables */
        SCB->DCCIMVAC = (unsigned int)&locals.sets;
        SCB->DCCIMVAC = (unsigned int)&locals.ways;
        SCB->DCCIMVAC = (unsigned int)&locals.ccsidr;
#else
        SCB->DCCIMVAC = (unsigned int)&locals;
#endif
        // __asm volatile("dsb 0xF" ::: "memory");
        // __asm volatile("isb 0xF" ::: "memory");
#endif

        locals.ccsidr = SCB->CCSIDR;
        /* clean & invalidate D-Cache */
        locals.sets = (unsigned int)(CCSIDR_SETS(locals.ccsidr));
        do
        {
            locals.ways = (unsigned int)(CCSIDR_WAYS(locals.ccsidr));
            do
            {
                SCB->DCCISW = (((locals.sets << SCB_DCCISW_SET_Pos) & SCB_DCCISW_SET_Msk) |
                               ((locals.ways << SCB_DCCISW_WAY_Pos) & SCB_DCCISW_WAY_Msk));
#if defined(__CC_ARM)
                __schedule_barrier();
#endif
            } while (locals.ways-- != 0U);
        } while (locals.sets-- != 0U);

        // __asm volatile("dsb 0xF" ::: "memory");
        // __asm volatile("isb 0xF" ::: "memory");

        unsigned int ccsidr;
        unsigned int sets;
        unsigned int ways;

        if (SCB->CCR & SCB_CCR_DC_Msk)
            return; /* return if DCache is already enabled */

        SCB->CSSELR = 0U; /* select Level 1 data cache */
        // __asm volatile("dsb 0xF" ::: "memory");

        ccsidr = SCB->CCSIDR;

        /* invalidate D-Cache */
        sets = (unsigned int)(CCSIDR_SETS(ccsidr));
        do
        {
            ways = (unsigned int)(CCSIDR_WAYS(ccsidr));
            do
            {
                SCB->DCISW = (((sets << SCB_DCISW_SET_Pos) & SCB_DCISW_SET_Msk) |
                              ((ways << SCB_DCISW_WAY_Pos) & SCB_DCISW_WAY_Msk));
#if defined(__CC_ARM)
                __schedule_barrier();
#endif
            } while (ways-- != 0U);
        } while (sets-- != 0U);
        // __asm volatile("dsb 0xF" ::: "memory");

        SCB->CCR |= (unsigned int)SCB_CCR_DC_Msk; /* enable D-Cache */

        // __asm volatile("dsb 0xF" ::: "memory");
        // __asm volatile("isb 0xF" ::: "memory");
#endif
    }
    EALLOW;
    HWREG(FLASH_R_BASE + FLASH_FRD_INTF_CTRL) |= (FLASH_FRD_INTF_CTRL_ART0_ICRST | FLASH_FRD_INTF_CTRL_ART0_DCRST | FLASH_FRD_INTF_CTRL_ART1_ICRST | FLASH_FRD_INTF_CTRL_ART1_DCRST | FLASH_FRD_INTF_CTRL_BUS_BUF_CLR);
    HWREG(FLASH_R_BASE + FLASH_FRD_INTF_CTRL) &= ~(FLASH_FRD_INTF_CTRL_ART0_ICRST | FLASH_FRD_INTF_CTRL_ART0_DCRST | FLASH_FRD_INTF_CTRL_ART1_ICRST | FLASH_FRD_INTF_CTRL_ART1_DCRST | FLASH_FRD_INTF_CTRL_BUS_BUF_CLR);
    EDIS;
}

/**
 * @brief  Returns the value of the FMSTAT register.
 * @param  None.
 * @retval FMSTAT register value.
 */
unsigned int Fapi_getFsmStatus(void)
{
    return HWREG(FLASH_R_BASE + FLASH_FMSTAT);
}

/**
 * @brief  Verifies region specified is erased value.
 * @param  pu32StartAddress:start address for region to blank check.
 * @param  u32Length:length of region in 32-bit words to blank check.
 * @param  poFlashStatusWord : returns the status of the operation if result is not Fapi_Status_Success.
 *            ->au32StatusWord[0] address of first non-blank location.
 *            ->au32StatusWord[1] data read at first non-blank location.
 *            ->au32StatusWord[2] value of compare data (always 0xFFFFFFFF).
 *            ->au32StatusWord[3] always 1.
 * @retval Fapi_Status_Success or Fapi_Error_Fail
 */
Fapi_StatusType Fapi_doBlankCheck(
    unsigned int *pu32StartAddress,
    unsigned int u32Length,
    Fapi_FlashStatusWordType *poFlashStatusWord)
{
    unsigned int adr = (unsigned int)pu32StartAddress;
    unsigned int val;
    // ECC region only 16bit read
    if ((adr >= ECC_STARTADR) && (adr <= ECC_ENDADR))
    {
        for (; adr < ((unsigned int)pu32StartAddress + 4 * u32Length); adr += 4)
        {
            val = *(unsigned short *)adr;
            val += (*(unsigned short *)(adr + 0x2) << 16);
            if (val != 0xFFFFFFFF)
            {
                poFlashStatusWord->au32StatusWord[0] = adr;
                poFlashStatusWord->au32StatusWord[1] = val;
                poFlashStatusWord->au32StatusWord[2] = 0xFFFFFFFF;
                poFlashStatusWord->au32StatusWord[3] = 1;
                return Fapi_Error_Fail;
            }
        }
    }
    else
    {
        for (; adr < ((unsigned int)pu32StartAddress + 4 * u32Length); adr += 4)
        {
            val = *(unsigned int *)adr;
            if (val != 0xFFFFFFFF)
            {
                poFlashStatusWord->au32StatusWord[0] = adr;
                poFlashStatusWord->au32StatusWord[1] = val;
                poFlashStatusWord->au32StatusWord[2] = 0xFFFFFFFF;
                poFlashStatusWord->au32StatusWord[3] = 1;
                return Fapi_Error_Fail;
            }
        }
    }

    return Fapi_Status_Success;
}

/**
 * @brief  Sets up data and issues program command to valid Flash or OTP(Only DCSM) memory addresses(All Bank Mode).
 * @param  pu32StartAddress:Start address in Flash for the data and ECC to be programmed.
 * @param  pu32DataBuffer:Pointer to the Data buffer address.
 * @param  u32DataBufferSizeInWords:Number of 32-bit words in the Data buffer.
 * @param  pu16EccBuffer:Pointer to the ECC buffer address.
 * @param  u16EccBufferSizeInBytes:Number of 8-bit bytes in the ECC buffer.
 * @retval Fapi_StatusType, The returned value can be
 *            Fapi_Error_FeatureNotAvailable,
 *            Fapi_Error_FlashRegsNotWritable,
 *            Fapi_Error_AsyncIncorrectDataBufferLength,
 *            Fapi_Error_AsyncIncorrectEccBufferLength,
 *            Fapi_Error_AsyncDataEccBufferLengthMismatch,
 *            Fapi_Error_InvalidAddress,
 *            Fapi_Status_Success,
 */
Fapi_StatusType Fapi_issueProgrammingCommand(
    unsigned int *pu32StartAddress,
    unsigned int *pu32DataBuffer,
    unsigned int u32DataBufferSizeInWords,
    unsigned short *pu16EccBuffer,
    unsigned short u16EccBufferSizeInBytes,
    Fapi_FlashProgrammingCommandsType oMode)
{
    unsigned int Address = (unsigned int)pu32StartAddress;
    unsigned int *Data, UserEndadr;
    unsigned int tmp, index, ECCValue;

    Fapi_StatusType StatusType;

    Data = pu32DataBuffer;

    // Tcm addr
    if ((Address >= 0x00100000) && (Address <= 0x0019FFFF))
    {
        Address = Address - 0x00100000 + 0x08000000;
    }
    else if ((Address >= 0x00080000) && (Address <= 0x0008BFFF))
    {
        Address = Address - 0x00080000 + 0x08100000;
    }
    // User Accessable FLASH
    UserEndadr = HWREG(0x0810AFF0);
    if (UserEndadr > 0x080A0000)
        UserEndadr = 0x080A0000;

    // oMode Error
    if ((oMode != Fapi_EccOnly) && (oMode != Fapi_DataAndEcc) && (oMode != Fapi_AutoEccGeneration) && (oMode != Fapi_DataOnly))
        return Fapi_Error_FeatureNotAvailable;

    // whether NVMC register writable
    if ((HWREG(FLASH_R_BASE + FLASH_FMSTAT) & FLASH_FMSTAT_NOT_WRITABLE) != 0)
        return Fapi_Error_FlashRegsNotWritable;

    // data length exceed 128bit boundary
    if ((oMode != Fapi_EccOnly))
    {
        if (((Address & 0xF) + (u32DataBufferSizeInWords << 2)) > BANKWIDTHINBYTES)
        {
            StatusType = Fapi_Error_AsyncIncorrectDataBufferLength;
            return StatusType;
        }
    }
    // data size not right
    if ((oMode == Fapi_DataAndEcc) || (oMode == Fapi_AutoEccGeneration))
    {
        if ((u32DataBufferSizeInWords != 2) && (u32DataBufferSizeInWords != 4))
        {
            StatusType = Fapi_Error_AsyncIncorrectDataBufferLength;
            return StatusType;
        }
    }
    // ecc size not right or ecc length exceed 128bit boundary
    if ((oMode == Fapi_DataAndEcc) || (oMode == Fapi_EccOnly))
    {
        if ((u16EccBufferSizeInBytes != 1) && (u16EccBufferSizeInBytes != 2))
        {
            StatusType = Fapi_Error_AsyncIncorrectEccBufferLength;
            return StatusType;
        }
        if (((Address & 0xF) + (u16EccBufferSizeInBytes << 3)) > BANKWIDTHINBYTES)
        {
            StatusType = Fapi_Error_AsyncIncorrectEccBufferLength;
            return StatusType;
        }
    }
    // dataAndEcc mode,data lenth dismatch with ecc lenth
    if (oMode == Fapi_DataAndEcc)
    {
        if (u16EccBufferSizeInBytes != (u32DataBufferSizeInWords >> 1))
            return Fapi_Error_AsyncDataEccBufferLengthMismatch;
    }
    // dataOnly mode,data lenth = 0
    if (oMode == Fapi_DataOnly)
    {
        if (u32DataBufferSizeInWords == 0)
            return Fapi_Error_AsyncIncorrectDataBufferLength;
    }
    // Linkpoint
    if (((Address >= 0x0810A000) && (Address <= 0x0810A017)) || ((Address >= 0x0810A400) && (Address <= 0x0810A417)) ||
        ((Address >= 0x0810B000) && (Address <= 0x0810B017)) || ((Address >= 0x0810B400) && (Address <= 0x0810B417)))
    {
        if (oMode == Fapi_EccOnly)
            return Fapi_Error_FeatureNotAvailable;
        else
            oMode = Fapi_DataOnly;
    }

    // Adress not 64bit/32bit align
    if (oMode == Fapi_DataOnly)
    {
        if (Address % 4)
            return Fapi_Error_InvalidAddress;
    }
    else
    {
        if (Address % 8)
            return Fapi_Error_InvalidAddress;
    }

    // Clear ERROR flag
    Fapi_issueAsyncCommand(Fapi_ClearStatus);
    // Wait busy
    while (Fapi_checkFsmForReady() == Fapi_Status_FsmBusy)
        ;

    EALLOW;
    if (HWREG(FLASH_R_BASE + FLASH_CR) & FLASH_CR_NBANK) // Single Bank Mode
    {
        if ((Address >= SINGLEMODE_BANK0_STARTADR) && (Address <= SINGLEMODE_BANK0_ENDADR))
        {
            HWREG(FLASH_R_BASE + FLASH_SMW_CR) = 0;
            HWREG(FLASH_R_BASE + FLASH_SMW_CR) |= FLASH_SMW_CR_SMW_OP_PROGRAM;
            if ((oMode == Fapi_DataAndEcc) || (oMode == Fapi_EccOnly))
            {
                ECCValue = *pu16EccBuffer;
                HWREG(FLASH_R_BASE + FLASH_DIN8) = ECCValue;
            }
            if ((oMode == Fapi_AutoEccGeneration) || (oMode == Fapi_DataAndEcc))
            {
                if (oMode == Fapi_AutoEccGeneration)
                {
                    // enable encode
                    tmp = (HWREG(FLASH_R_BASE + FLASH_ECC_ENABLE) & ~(FLASH_ECC_ENABLE_ENCODE_M));
                    HWREG(FLASH_R_BASE + FLASH_ECC_ENABLE) = (ECC_ENCODE_ENABLE | tmp);
                }
                else if (oMode == Fapi_DataAndEcc)
                {
                    // disable encode
                    HWREG(FLASH_R_BASE + FLASH_ECC_ENABLE) &= ~(FLASH_ECC_ENABLE_ENCODE_M);
                }
                // data lenth
                if (u32DataBufferSizeInWords == 4)
                {
                    // bank0
                    if ((Address & BANKWIDTHINBYTES) == 0)
                    {
                        HWREG(FLASH_R_BASE + FLASH_DIN0) = Data[0];
                        HWREG(FLASH_R_BASE + FLASH_DIN1) = Data[1];
                        HWREG(FLASH_R_BASE + FLASH_DIN2) = Data[2];
                        HWREG(FLASH_R_BASE + FLASH_DIN3) = Data[3];
                        HWREG(FLASH_R_BASE + FLASH_PROG_CFG) = PROG_DATA_ECC_BANK0;
                    }
                    // bank1
                    else
                    {
                        HWREG(FLASH_R_BASE + FLASH_DIN4) = Data[0];
                        HWREG(FLASH_R_BASE + FLASH_DIN5) = Data[1];
                        HWREG(FLASH_R_BASE + FLASH_DIN6) = Data[2];
                        HWREG(FLASH_R_BASE + FLASH_DIN7) = Data[3];
                        HWREG(FLASH_R_BASE + FLASH_DIN8) = (ECCValue << 16);
                        HWREG(FLASH_R_BASE + FLASH_PROG_CFG) = PROG_DATA_ECC_BANK1;
                    }
                }
                else if (u32DataBufferSizeInWords == 2)
                {
                    if ((Address & BANKWIDTHINBYTES) == 0)
                    {
                        if ((Address & HALFBANKWIDTHINBYTES) == 0)
                        {
                            HWREG(FLASH_R_BASE + FLASH_DIN0) = Data[0];
                            HWREG(FLASH_R_BASE + FLASH_DIN1) = Data[1];
                            HWREG(FLASH_R_BASE + FLASH_PROG_CFG) = PROG_DATA_BANK0_0 | PROG_DATA_BANK0_4 | PROG_ECC_BANK0_L;
                        }
                        else
                        {
                            HWREG(FLASH_R_BASE + FLASH_DIN2) = Data[0];
                            HWREG(FLASH_R_BASE + FLASH_DIN3) = Data[1];
                            HWREG(FLASH_R_BASE + FLASH_PROG_CFG) = PROG_DATA_BANK0_8 | PROG_DATA_BANK0_C | PROG_ECC_BANK0_H;
                        }
                    }
                    else
                    {
                        if ((Address & HALFBANKWIDTHINBYTES) == 0)
                        {
                            HWREG(FLASH_R_BASE + FLASH_DIN4) = Data[0];
                            HWREG(FLASH_R_BASE + FLASH_DIN5) = Data[1];
                            HWREG(FLASH_R_BASE + FLASH_DIN8) = ECCValue << 16;
                            HWREG(FLASH_R_BASE + FLASH_PROG_CFG) = PROG_DATA_BANK1_0 | PROG_DATA_BANK1_4 | PROG_ECC_BANK1_L;
                        }
                        else
                        {
                            HWREG(FLASH_R_BASE + FLASH_DIN6) = Data[0];
                            HWREG(FLASH_R_BASE + FLASH_DIN7) = Data[1];
                            HWREG(FLASH_R_BASE + FLASH_DIN8) = ECCValue << 16;
                            HWREG(FLASH_R_BASE + FLASH_PROG_CFG) = PROG_DATA_BANK1_8 | PROG_DATA_BANK1_C | PROG_ECC_BANK1_H;
                        }
                    }
                }
            }
            else
            {
                // disable encode
                HWREG(FLASH_R_BASE + FLASH_ECC_ENABLE) &= ~(FLASH_ECC_ENABLE_ENCODE_M);

                if (oMode == Fapi_EccOnly)
                {
                    if (u16EccBufferSizeInBytes == 2)
                    {
                        // bank0
                        if ((Address & BANKWIDTHINBYTES) == 0)
                            HWREG(FLASH_R_BASE + FLASH_PROG_CFG) = PROG_ECC_BANK0_L | PROG_ECC_BANK0_H;
                        // bank1
                        else
                        {
                            HWREG(FLASH_R_BASE + FLASH_DIN8) = (ECCValue << 16);
                            HWREG(FLASH_R_BASE + FLASH_PROG_CFG) = PROG_ECC_BANK1_L | PROG_ECC_BANK1_H;
                        }
                    }
                    else if (u16EccBufferSizeInBytes == 1)
                    {
                        // bank0
                        if ((Address & BANKWIDTHINBYTES) == 0)
                        {
                            if ((Address & HALFBANKWIDTHINBYTES) == 0)
                            {
                                HWREG(FLASH_R_BASE + FLASH_PROG_CFG) = PROG_ECC_BANK0_L;
                            }
                            else
                            {
                                HWREG(FLASH_R_BASE + FLASH_PROG_CFG) = PROG_ECC_BANK0_H;
                            }
                        }
                        else
                        {
                            if ((Address & HALFBANKWIDTHINBYTES) == 0)
                            {
                                HWREG(FLASH_R_BASE + FLASH_DIN8) = (ECCValue << 16);
                                HWREG(FLASH_R_BASE + FLASH_PROG_CFG) = PROG_ECC_BANK1_L;
                            }
                            else
                            {
                                HWREG(FLASH_R_BASE + FLASH_DIN8) = (ECCValue << 16);
                                HWREG(FLASH_R_BASE + FLASH_PROG_CFG) = PROG_ECC_BANK1_H;
                            }
                        }
                    }
                }
                else if (oMode == Fapi_DataOnly)
                {
                    if ((Address & BANKWIDTHINBYTES) == 0)
                    {
                        index = (Address & 0xF) / 4;
                        HWREG(FLASH_R_BASE + FLASH_PROG_CFG) = 0;
                        HWREG(FLASH_R_BASE + FLASH_PROG_CFG) |= (1 << index);
                        HWREG(FLASH_R_BASE + 0x500 + index * 4) = Data[0];

                        // HWREG(FLASH_R_BASE + FLASH_PROG_CFG) Configure
                        if ((u32DataBufferSizeInWords >= 2) && ((index <= 2)))
                        {
                            HWREG(FLASH_R_BASE + FLASH_PROG_CFG) |= (1 << (index + 1));
                            HWREG(FLASH_R_BASE + 0x504 + index * 4) = Data[1];
                        }
                        if ((u32DataBufferSizeInWords >= 3) && ((index <= 1)))
                        {
                            HWREG(FLASH_R_BASE + FLASH_PROG_CFG) |= (1 << (index + 2));
                            HWREG(FLASH_R_BASE + 0x508 + index * 4) = Data[2];
                        }
                        if ((u32DataBufferSizeInWords == 4) && ((index == 0)))
                        {
                            HWREG(FLASH_R_BASE + FLASH_PROG_CFG) |= (1 << (index + 3));
                            HWREG(FLASH_R_BASE + 0x50C + index * 4) = Data[3];
                        }
                    }
                    else
                    {
                        index = (Address & 0xF) / 4;
                        HWREG(FLASH_R_BASE + FLASH_PROG_CFG) = 0;
                        HWREG(FLASH_R_BASE + FLASH_PROG_CFG) |= (1 << (index + 6));
                        HWREG(FLASH_R_BASE + 0x510 + index * 4) = Data[0];

                        // HWREG(FLASH_R_BASE + FLASH_PROG_CFG) Configure
                        if ((u32DataBufferSizeInWords >= 2) && ((index <= 2)))
                        {
                            HWREG(FLASH_R_BASE + FLASH_PROG_CFG) |= (1 << (index + 7));
                            HWREG(FLASH_R_BASE + 0x514 + index * 4) = Data[1];
                        }
                        if ((u32DataBufferSizeInWords >= 3) && ((index <= 1)))
                        {
                            HWREG(FLASH_R_BASE + FLASH_PROG_CFG) |= (1 << (index + 8));
                            HWREG(FLASH_R_BASE + 0x518 + index * 4) = Data[2];
                        }
                        if ((u32DataBufferSizeInWords == 4) && ((index == 0)))
                        {
                            HWREG(FLASH_R_BASE + FLASH_PROG_CFG) |= (1 << (index + 9));
                            HWREG(FLASH_R_BASE + 0x51C + index * 4) = Data[3];
                        }
                    }
                }
            }
            HWREG(FLASH_R_BASE + FLASH_FAR) = (Address & (~0x1F));
            HWREG(FLASH_R_BASE + FLASH_FLASH_OP) = (FLASH_FLASH_OP_0 | FLASH_FLASH_OP_1);
            FLASH_ClearCache_Buff();
        }
        else if ((Address >= SINGLEMODE_BANK1_STARTADR) && (Address < UserEndadr))
        {
            HWREG(FLASH_R_BASE + FLASH_SMW_CR) = 0;
            HWREG(FLASH_R_BASE + FLASH_SMW_CR) |= FLASH_SMW_CR_SMW_OP_PROGRAM;

            if ((oMode == Fapi_DataAndEcc) || (oMode == Fapi_EccOnly))
            {
                ECCValue = (*pu16EccBuffer) << 16;
                HWREG(FLASH_R_BASE + FLASH_DIN8) = ECCValue;
            }
            if ((oMode == Fapi_AutoEccGeneration) || (oMode == Fapi_DataAndEcc))
            {
                if (oMode == Fapi_AutoEccGeneration)
                {
                    // enable encode
                    tmp = (HWREG(FLASH_R_BASE + FLASH_ECC_ENABLE) & ~(FLASH_ECC_ENABLE_ENCODE_M));
                    HWREG(FLASH_R_BASE + FLASH_ECC_ENABLE) = (ECC_ENCODE_ENABLE | tmp);
                }
                else if (oMode == Fapi_DataAndEcc)
                {
                    // disable encode
                    HWREG(FLASH_R_BASE + FLASH_ECC_ENABLE) &= ~(FLASH_ECC_ENABLE_ENCODE_M);
                }
                // data lenth
                if (u32DataBufferSizeInWords == 4)
                {
                    HWREG(FLASH_R_BASE + FLASH_DIN4) = Data[0];
                    HWREG(FLASH_R_BASE + FLASH_DIN5) = Data[1];
                    HWREG(FLASH_R_BASE + FLASH_DIN6) = Data[2];
                    HWREG(FLASH_R_BASE + FLASH_DIN7) = Data[3];
                    HWREG(FLASH_R_BASE + FLASH_PROG_CFG) = PROG_DATA_ECC_BANK1;
                }
                else if (u32DataBufferSizeInWords == 2)
                {
                    if ((Address & HALFBANKWIDTHINBYTES) == 0)
                    {
                        HWREG(FLASH_R_BASE + FLASH_DIN4) = Data[0];
                        HWREG(FLASH_R_BASE + FLASH_DIN5) = Data[1];
                        HWREG(FLASH_R_BASE + FLASH_PROG_CFG) = PROG_DATA_BANK1_0 | PROG_DATA_BANK1_4 | PROG_ECC_BANK1_L;
                    }
                    else
                    {
                        HWREG(FLASH_R_BASE + FLASH_DIN6) = Data[0];
                        HWREG(FLASH_R_BASE + FLASH_DIN7) = Data[1];
                        HWREG(FLASH_R_BASE + FLASH_PROG_CFG) = PROG_DATA_BANK1_8 | PROG_DATA_BANK1_C | PROG_ECC_BANK1_H;
                    }
                }
            }
            else
            {
                // disable encode
                HWREG(FLASH_R_BASE + FLASH_ECC_ENABLE) &= ~(FLASH_ECC_ENABLE_ENCODE_M);

                if (oMode == Fapi_EccOnly)
                {
                    if (u16EccBufferSizeInBytes == 2)
                        HWREG(FLASH_R_BASE + FLASH_PROG_CFG) = PROG_ECC_BANK1_L | PROG_ECC_BANK1_H;
                    else if (u16EccBufferSizeInBytes == 1)
                    {
                        if ((Address & HALFBANKWIDTHINBYTES) == 0)
                            HWREG(FLASH_R_BASE + FLASH_PROG_CFG) = PROG_ECC_BANK1_L;
                        else
                        {
                            HWREG(FLASH_R_BASE + FLASH_PROG_CFG) = PROG_ECC_BANK1_H;
                        }
                    }
                }
                else if (oMode == Fapi_DataOnly)
                {
                    index = (Address & 0xF) / 4;
                    HWREG(FLASH_R_BASE + FLASH_PROG_CFG) = 0;
                    HWREG(FLASH_R_BASE + FLASH_PROG_CFG) |= (1 << (index + 6));
                    HWREG(FLASH_R_BASE + 0x510 + index * 4) = Data[0];

                    // HWREG(FLASH_R_BASE + FLASH_PROG_CFG) Configure
                    if ((u32DataBufferSizeInWords >= 2) && ((index <= 2)))
                    {
                        HWREG(FLASH_R_BASE + FLASH_PROG_CFG) |= (1 << (index + 7));
                        HWREG(FLASH_R_BASE + 0x514 + index * 4) = Data[1];
                    }
                    if ((u32DataBufferSizeInWords >= 3) && ((index <= 1)))
                    {
                        HWREG(FLASH_R_BASE + FLASH_PROG_CFG) |= (1 << (index + 8));
                        HWREG(FLASH_R_BASE + 0x518 + index * 4) = Data[2];
                    }

                    if ((u32DataBufferSizeInWords == 4) && ((index == 0)))
                    {
                        HWREG(FLASH_R_BASE + FLASH_PROG_CFG) |= (1 << (index + 9));
                        HWREG(FLASH_R_BASE + 0x51C + index * 4) = Data[3];
                    }
                }
            }
            HWREG(FLASH_R_BASE + FLASH_FAR) = (Address & (~0xF));
            HWREG(FLASH_R_BASE + FLASH_FLASH_OP) = (FLASH_FLASH_OP_1);
            FLASH_ClearCache_Buff();
        }
        else if (((Address >= DCSM0_STARTADR) && (Address <= DCSM0_ENDADR)) || ((Address >= DCSM1_STARTADR) && (Address <= DCSM1_ENDADR)))
            ;
        else
        {
            EDIS;
            return Fapi_Error_InvalidAddress;
        }
    }
    else // Dual Bank Mode
    {
        // Bank0
        if ((Address >= DUALMODE_BANK0_STARTADR) && (Address <= DUALMODE_BANK0_ENDADR))
        {
            HWREG(FLASH_R_BASE + FLASH_SMW_CR) = 0;
            HWREG(FLASH_R_BASE + FLASH_SMW_CR) |= FLASH_SMW_CR_SMW_OP_PROGRAM;

            if ((oMode == Fapi_DataAndEcc) || (oMode == Fapi_EccOnly))
            {
                ECCValue = *pu16EccBuffer;
                HWREG(FLASH_R_BASE + FLASH_DIN8) = ECCValue;
            }
            if ((oMode == Fapi_AutoEccGeneration) || (oMode == Fapi_DataAndEcc))
            {
                if (oMode == Fapi_AutoEccGeneration)
                {
                    // enable encode
                    tmp = (HWREG(FLASH_R_BASE + FLASH_ECC_ENABLE) & ~(FLASH_ECC_ENABLE_ENCODE_M));
                    HWREG(FLASH_R_BASE + FLASH_ECC_ENABLE) = (ECC_ENCODE_ENABLE | tmp);
                }
                else if (oMode == Fapi_DataAndEcc)
                {
                    // disable encode
                    HWREG(FLASH_R_BASE + FLASH_ECC_ENABLE) &= ~(FLASH_ECC_ENABLE_ENCODE_M);
                }
                // data lenth
                if (u32DataBufferSizeInWords == 4)
                {
                    HWREG(FLASH_R_BASE + FLASH_DIN0) = Data[0];
                    HWREG(FLASH_R_BASE + FLASH_DIN1) = Data[1];
                    HWREG(FLASH_R_BASE + FLASH_DIN2) = Data[2];
                    HWREG(FLASH_R_BASE + FLASH_DIN3) = Data[3];
                    HWREG(FLASH_R_BASE + FLASH_PROG_CFG) = PROG_DATA_ECC_BANK0;
                }
                else if (u32DataBufferSizeInWords == 2)
                {
                    if ((Address & HALFBANKWIDTHINBYTES) == 0)
                    {
                        HWREG(FLASH_R_BASE + FLASH_DIN0) = Data[0];
                        HWREG(FLASH_R_BASE + FLASH_DIN1) = Data[1];
                        HWREG(FLASH_R_BASE + FLASH_PROG_CFG) = PROG_DATA_BANK0_0 | PROG_DATA_BANK0_4 | PROG_ECC_BANK0_L;
                    }
                    else
                    {
                        HWREG(FLASH_R_BASE + FLASH_DIN2) = Data[0];
                        HWREG(FLASH_R_BASE + FLASH_DIN3) = Data[1];
                        HWREG(FLASH_R_BASE + FLASH_PROG_CFG) = PROG_DATA_BANK0_8 | PROG_DATA_BANK0_C | PROG_ECC_BANK0_H;
                    }
                }
            }
            else
            {
                // disable encode
                HWREG(FLASH_R_BASE + FLASH_ECC_ENABLE) &= ~(FLASH_ECC_ENABLE_ENCODE_M);

                if (oMode == Fapi_EccOnly)
                {
                    if (u16EccBufferSizeInBytes == 2)
                        HWREG(FLASH_R_BASE + FLASH_PROG_CFG) = PROG_ECC_BANK0_L | PROG_ECC_BANK0_H;
                    else if (u16EccBufferSizeInBytes == 1)
                    {
                        if ((Address & HALFBANKWIDTHINBYTES) == 0)
                            HWREG(FLASH_R_BASE + FLASH_PROG_CFG) = PROG_ECC_BANK0_L;
                        else
                        {
                            HWREG(FLASH_R_BASE + FLASH_PROG_CFG) = PROG_ECC_BANK0_H;
                        }
                    }
                }
                else if (oMode == Fapi_DataOnly)
                {
                    index = (Address & 0xF) / 4;
                    HWREG(FLASH_R_BASE + FLASH_PROG_CFG) = 0;
                    HWREG(FLASH_R_BASE + FLASH_PROG_CFG) |= (1 << index);
                    HWREG(FLASH_R_BASE + 0x500 + index * 4) = Data[0];

                    // HWREG(FLASH_R_BASE + FLASH_PROG_CFG) Configure
                    if ((u32DataBufferSizeInWords >= 2) && ((index <= 2)))
                    {
                        HWREG(FLASH_R_BASE + FLASH_PROG_CFG) |= (1 << (index + 1));
                        HWREG(FLASH_R_BASE + 0x504 + index * 4) = Data[1];
                    }
                    if ((u32DataBufferSizeInWords >= 3) && ((index <= 1)))
                    {
                        HWREG(FLASH_R_BASE + FLASH_PROG_CFG) |= (1 << (index + 2));
                        HWREG(FLASH_R_BASE + 0x508 + index * 4) = Data[2];
                    }
                    if ((u32DataBufferSizeInWords == 4) && ((index == 0)))
                    {
                        HWREG(FLASH_R_BASE + FLASH_PROG_CFG) |= (1 << (index + 3));
                        HWREG(FLASH_R_BASE + 0x50C + index * 4) = Data[3];
                    }
                }
            }

            HWREG(FLASH_R_BASE + FLASH_FAR) = (Address & (~0xF));
            HWREG(FLASH_R_BASE + FLASH_FLASH_OP) = (FLASH_FLASH_OP_0);
            FLASH_ClearCache_Buff();
        }
        // Bank1
        else if ((Address >= DUALMODE_BANK1_STARTADR) && (Address < UserEndadr))
        {
            HWREG(FLASH_R_BASE + FLASH_SMW_CR) = 0;
            HWREG(FLASH_R_BASE + FLASH_SMW_CR) |= FLASH_SMW_CR_SMW_OP_PROGRAM;

            if ((oMode == Fapi_DataAndEcc) || (oMode == Fapi_EccOnly))
            {
                ECCValue = (*pu16EccBuffer) << 16;
                HWREG(FLASH_R_BASE + FLASH_DIN8) = ECCValue;
            }
            if ((oMode == Fapi_AutoEccGeneration) || (oMode == Fapi_DataAndEcc))
            {
                if (oMode == Fapi_AutoEccGeneration)
                {
                    // enable encode
                    tmp = (HWREG(FLASH_R_BASE + FLASH_ECC_ENABLE) & ~(FLASH_ECC_ENABLE_ENCODE_M));
                    HWREG(FLASH_R_BASE + FLASH_ECC_ENABLE) = (ECC_ENCODE_ENABLE | tmp);
                }
                else if (oMode == Fapi_DataAndEcc)
                {
                    // disable encode
                    HWREG(FLASH_R_BASE + FLASH_ECC_ENABLE) &= ~(FLASH_ECC_ENABLE_ENCODE_M);
                }
                // data lenth
                if (u32DataBufferSizeInWords == 4)
                {
                    HWREG(FLASH_R_BASE + FLASH_DIN4) = Data[0];
                    HWREG(FLASH_R_BASE + FLASH_DIN5) = Data[1];
                    HWREG(FLASH_R_BASE + FLASH_DIN6) = Data[2];
                    HWREG(FLASH_R_BASE + FLASH_DIN7) = Data[3];
                    HWREG(FLASH_R_BASE + FLASH_PROG_CFG) = PROG_DATA_ECC_BANK1;
                }
                else if (u32DataBufferSizeInWords == 2)
                {
                    if ((Address & HALFBANKWIDTHINBYTES) == 0)
                    {
                        HWREG(FLASH_R_BASE + FLASH_DIN4) = Data[0];
                        HWREG(FLASH_R_BASE + FLASH_DIN5) = Data[1];
                        HWREG(FLASH_R_BASE + FLASH_PROG_CFG) = PROG_DATA_BANK1_0 | PROG_DATA_BANK1_4 | PROG_ECC_BANK1_L;
                    }
                    else
                    {
                        HWREG(FLASH_R_BASE + FLASH_DIN6) = Data[0];
                        HWREG(FLASH_R_BASE + FLASH_DIN7) = Data[1];
                        HWREG(FLASH_R_BASE + FLASH_PROG_CFG) = PROG_DATA_BANK1_8 | PROG_DATA_BANK1_C | PROG_ECC_BANK1_H;
                    }
                }
            }
            else
            {
                // disable encode
                HWREG(FLASH_R_BASE + FLASH_ECC_ENABLE) &= ~(FLASH_ECC_ENABLE_ENCODE_M);

                if (oMode == Fapi_EccOnly)
                {
                    if (u16EccBufferSizeInBytes == 2)
                        HWREG(FLASH_R_BASE + FLASH_PROG_CFG) = PROG_ECC_BANK1_L | PROG_ECC_BANK1_H;
                    else if (u16EccBufferSizeInBytes == 1)
                    {
                        if ((Address & HALFBANKWIDTHINBYTES) == 0)
                            HWREG(FLASH_R_BASE + FLASH_PROG_CFG) = PROG_ECC_BANK1_L;
                        else
                        {
                            HWREG(FLASH_R_BASE + FLASH_PROG_CFG) = PROG_ECC_BANK1_H;
                        }
                    }
                }
                else if (oMode == Fapi_DataOnly)
                {
                    index = (Address & 0xF) / 4;
                    HWREG(FLASH_R_BASE + FLASH_PROG_CFG) = 0;
                    HWREG(FLASH_R_BASE + FLASH_PROG_CFG) |= (1 << (index + 6));
                    HWREG(FLASH_R_BASE + 0x510 + index * 4) = Data[0];

                    // HWREG(FLASH_R_BASE + FLASH_PROG_CFG) Configure
                    if ((u32DataBufferSizeInWords >= 2) && ((index <= 2)))
                    {
                        HWREG(FLASH_R_BASE + FLASH_PROG_CFG) |= (1 << (index + 7));
                        HWREG(FLASH_R_BASE + 0x514 + index * 4) = Data[1];
                    }
                    if ((u32DataBufferSizeInWords >= 3) && ((index <= 1)))
                    {
                        HWREG(FLASH_R_BASE + FLASH_PROG_CFG) |= (1 << (index + 8));
                        HWREG(FLASH_R_BASE + 0x518 + index * 4) = Data[2];
                    }
                    if ((u32DataBufferSizeInWords == 4) && ((index == 0)))
                    {
                        HWREG(FLASH_R_BASE + FLASH_PROG_CFG) |= (1 << (index + 9));
                        HWREG(FLASH_R_BASE + 0x51C + index * 4) = Data[3];
                    }
                }
            }
            HWREG(FLASH_R_BASE + FLASH_FAR) = (Address & (~0xF));
            HWREG(FLASH_R_BASE + FLASH_FLASH_OP) = (FLASH_FLASH_OP_1);
            FLASH_ClearCache_Buff();
        }
        else if (((Address >= DCSM0_STARTADR) && (Address <= DCSM0_ENDADR)) || ((Address >= DCSM1_STARTADR) && (Address <= DCSM1_ENDADR)))
            ;
        else
        {
            EDIS;
            return Fapi_Error_InvalidAddress;
        }
    }
    if (((Address >= DCSM0_STARTADR) && (Address <= DCSM0_ENDADR)) || ((Address >= DCSM1_STARTADR) && (Address <= DCSM1_ENDADR)))
    {
        HWREG(FLASH_R_BASE + FLASH_SMW_CR) = 0;
        HWREG(FLASH_R_BASE + FLASH_SMW_CR) |= FLASH_SMW_CR_SMW_OP_PROGRAM;

        if ((oMode == Fapi_DataAndEcc) || (oMode == Fapi_EccOnly))
        {
            ECCValue = (*pu16EccBuffer) << 16;
            HWREG(FLASH_R_BASE + FLASH_DIN8) = ECCValue;
        }
        if ((oMode == Fapi_AutoEccGeneration) || (oMode == Fapi_DataAndEcc))
        {
            if (oMode == Fapi_AutoEccGeneration)
            {
                // enable encode
                tmp = (HWREG(FLASH_R_BASE + FLASH_ECC_ENABLE) & ~(FLASH_ECC_ENABLE_ENCODE_M));
                HWREG(FLASH_R_BASE + FLASH_ECC_ENABLE) = (ECC_ENCODE_ENABLE | tmp);
            }
            else if (oMode == Fapi_DataAndEcc)
            {
                // disable encode
                HWREG(FLASH_R_BASE + FLASH_ECC_ENABLE) &= ~(FLASH_ECC_ENABLE_ENCODE_M);
            }
            // data lenth
            if (u32DataBufferSizeInWords == 4)
            {
                HWREG(FLASH_R_BASE + FLASH_DIN4) = Data[0];
                HWREG(FLASH_R_BASE + FLASH_DIN5) = Data[1];
                HWREG(FLASH_R_BASE + FLASH_DIN6) = Data[2];
                HWREG(FLASH_R_BASE + FLASH_DIN7) = Data[3];
                HWREG(FLASH_R_BASE + FLASH_PROG_CFG) = PROG_DATA_ECC_BANK1;
            }
            else if (u32DataBufferSizeInWords == 2)
            {
                if ((Address & HALFBANKWIDTHINBYTES) == 0)
                {
                    HWREG(FLASH_R_BASE + FLASH_DIN4) = Data[0];
                    HWREG(FLASH_R_BASE + FLASH_DIN5) = Data[1];
                    HWREG(FLASH_R_BASE + FLASH_PROG_CFG) = PROG_DATA_BANK1_0 | PROG_DATA_BANK1_4 | PROG_ECC_BANK1_L;
                }
                else
                {
                    HWREG(FLASH_R_BASE + FLASH_DIN6) = Data[0];
                    HWREG(FLASH_R_BASE + FLASH_DIN7) = Data[1];
                    HWREG(FLASH_R_BASE + FLASH_PROG_CFG) = PROG_DATA_BANK1_8 | PROG_DATA_BANK1_C | PROG_ECC_BANK1_H;
                }
            }
        }
        else
        {
            // disable encode
            HWREG(FLASH_R_BASE + FLASH_ECC_ENABLE) &= ~(FLASH_ECC_ENABLE_ENCODE_M);

            if (oMode == Fapi_EccOnly)
            {
                if (u16EccBufferSizeInBytes == 2)
                    HWREG(FLASH_R_BASE + FLASH_PROG_CFG) = PROG_ECC_BANK1_L | PROG_ECC_BANK1_H;
                else if (u16EccBufferSizeInBytes == 1)
                {
                    if ((Address & HALFBANKWIDTHINBYTES) == 0)
                        HWREG(FLASH_R_BASE + FLASH_PROG_CFG) = PROG_ECC_BANK1_L;
                    else
                    {
                        HWREG(FLASH_R_BASE + FLASH_PROG_CFG) = PROG_ECC_BANK1_H;
                    }
                }
            }
            else if (oMode == Fapi_DataOnly)
            {
                index = (Address & 0xF) / 4;
                HWREG(FLASH_R_BASE + FLASH_PROG_CFG) = 0;
                HWREG(FLASH_R_BASE + FLASH_PROG_CFG) |= (1 << (index + 6));
                HWREG(FLASH_R_BASE + 0x510 + index * 4) = Data[0];

                // HWREG(FLASH_R_BASE + FLASH_PROG_CFG) Configure
                if ((u32DataBufferSizeInWords >= 2) && ((index <= 2)))
                {
                    HWREG(FLASH_R_BASE + FLASH_PROG_CFG) |= (1 << (index + 7));
                    HWREG(FLASH_R_BASE + 0x514 + index * 4) = Data[1];
                }
                if ((u32DataBufferSizeInWords >= 3) && ((index <= 1)))
                {
                    HWREG(FLASH_R_BASE + FLASH_PROG_CFG) |= (1 << (index + 8));
                    HWREG(FLASH_R_BASE + 0x518 + index * 4) = Data[2];
                }
                if ((u32DataBufferSizeInWords == 4) && ((index == 0)))
                {
                    HWREG(FLASH_R_BASE + FLASH_PROG_CFG) |= (1 << (index + 9));
                    HWREG(FLASH_R_BASE + 0x51C + index * 4) = Data[3];
                }
            }
        }
        HWREG(FLASH_R_BASE + FLASH_SMW_CR) |= (FLASH_SMW_CR_SMW_ARRAY_INFO);
        HWREG(FLASH_R_BASE + FLASH_FAR) = (Address & (~0xF));
        HWREG(FLASH_R_BASE + FLASH_FLASH_OP) = (FLASH_FLASH_OP_1);
        FLASH_ClearCache_Buff();
    }
    EDIS;
    StatusType = Fapi_Status_Success;
    return StatusType;
}

static int g32x_is_otp(struct flash_bank *bank)
{
    return (bank->base == G32R5_OTP_BANK_BASE);
}

static int g32x_is_flash_otp(struct flash_bank *bank)
{
    return (bank->base == G32R5_OTP_BANK_BASE || bank->base == G32R5_FLASH_BANK_BASE);
}

static int Fapi_initializeAPI(unsigned int Hclkf)
{
    SYSCLK = Hclkf;
    return Fapi_Status_Success;
}

/*
 *  Initialize Flash Programming Functions
 *    Parameter:      adr:  Device Base Address
 *                    clk:  Clock Frequency (Hz)
 *                    fnc:  Function Code (1 - Erase, 2 - Program, 3 - Verify)
 *    Return Value:   0 - OK,  1 - Failed
 */
static int Init(unsigned long adr, unsigned long clk, unsigned long fnc)
{
    Fapi_StatusType retCheck;

    LOG_WARNING("Init: Enable Eallow Protection COP");
    *(unsigned int *)(0xE000ED88) |= 0x0C; // Enable Eallow Protection COP
    // __arm_mcr(1, 0, 1, 0, 12, 0);
    LOG_WARNING("Init: Finish enable Eallow Protection COP");
    *(unsigned short *)0x50026452U |= 0x0068;    // disable WDG
    // __arm_mcr(1, 0, 0, 0, 12, 0);
    LOG_WARNING("Init: Disable WDG");

    if (g32x_is_flash_otp(adr))
    {
        retCheck = Fapi_initializeAPI(50);
        LOG_WARNING("Init: Finish Fapi_initializeAPI");
        if(retCheck != Fapi_Status_Success)
        {
            return 1;
        }

        /* Initialize the Flash banks and FMC for erase and program operations. */
        retCheck = Fapi_setActiveFlashBank(Fapi_FlashBank0); // fullmask chip return Fapi_Error_OtpChecksumMismatch error
        LOG_WARNING("Init: Finish Fapi_setActiveFlashBank");
        if(retCheck != Fapi_Status_Success)
        {
            return 1;
        }
    }
    LOG_WARNING("Init: Finish g32x_is_flash_otp");

    /* Add your Code */
    return (0);                                 // Finished without Errors
}

/* flash bank g32x <base> <size> 0 0 <target#>
 */
FLASH_BANK_COMMAND_HANDLER(g32x_flash_bank_command)
{
    struct g32x_flash_bank *g32x_info;

    if (CMD_ARGC < 6)
        return ERROR_COMMAND_SYNTAX_ERROR;

    g32x_info = malloc(sizeof(struct g32x_flash_bank));

    bank->driver_priv = g32x_info;
    g32x_info->probed = false;
    g32x_info->user_bank_size = bank->size;

    LOG_WARNING("Bank: Finish flash bank g32x");
    return ERROR_OK;
}

#define G32R5_SECTOR_SIZE 0x2000
#define G32R5_SECTOR_START 0x08000000

/*
 *  Program Page in Flash Memory
 *    Parameter:      adr:  Page Start Address
 *                    sz:   Page Size
 *                    buf:  Page Data
 *    Return Value:   0 - OK,  1 - Failed
 */
static int ProgramPage(unsigned long adr, unsigned long sz, unsigned char *buf)
{
    Fapi_StatusType retCheck;

    sz = sz + (8 - sz % 8) % 8; // 8 Byte align

    while (sz)
    {
        if (!g32x_is_otp(adr))
        {
            // 2 * unsigned int
            retCheck = Fapi_issueProgrammingCommand((unsigned int *)adr, (unsigned int *)buf, 2, 0, 0, Fapi_AutoEccGeneration);
            LOG_WARNING("Program: Finish Fapi_issueProgrammingCommand");

            // Wait until the Flash program operation is over
            while (Fapi_checkFsmForReady() != Fapi_Status_FsmReady)
            {
            }
            LOG_WARNING("Program: Finish Fapi_checkFsmForReady");

            if (retCheck != Fapi_Status_Success)
            {
                return 1;
            }
            LOG_WARNING("Program: Pass Fapi_issueProgrammingCommand");

            // Read FMSTAT register contents to know the status of FSM after
            // erase command to see if there are any erase operation related errors
            if (Fapi_getFsmStatus() != 0)
            {
                return 1;
            }
            LOG_WARNING("Program: Pass check");
        }
        else
        { // program RAM
            *(unsigned int *)adr = *(unsigned int *)buf;
            *(unsigned int *)(adr + 4) = *(unsigned int *)(buf + 4);
            LOG_WARNING("Program: Finish RAM program");
        }

        sz -= 8;
        adr += 8;
        buf += 8;
        LOG_WARNING("Program: Last size %d", sz);
    }

    return (0); // Finished without Errors
}

static int g32x_write(struct flash_bank *bank, const uint8_t *buffer,
                      unsigned int offset, unsigned int count)
{
    struct target *target = bank->target;

    LOG_WARNING("enter into g32x_write function");

    if (bank->target->state != TARGET_HALTED)
    {
        LOG_ERROR("Target not halted");
        return ERROR_TARGET_NOT_HALTED;
    }
    LOG_WARNING("g32x_write: finish halt");

    Init(G32R5_FLASH_BANK_BASE, 0, 0);
    LOG_WARNING("g32x_write: finish init");

    int retval;

    /* write to flash */
    retval = ProgramPage(bank->base + offset, count, buffer);
    LOG_WARNING("g32x_write: finish programpage");

    return retval;
}

static int EraseSector(unsigned long adr)
{
    Fapi_StatusType retCheck;
    Fapi_FlashStatusWordType oFlashStatusWord;

    if (!g32x_is_otp(adr))
    {
        // Erase Flash sector
        retCheck = Fapi_issueAsyncCommandWithAddress(Fapi_EraseSector, (unsigned int *)adr);
        LOG_WARNING("EraaseSector: finish Fapi_issueAsyncCommandWithAddress");

        // Wait until FSM is done with erase sector operation
        while (Fapi_checkFsmForReady() != Fapi_Status_FsmReady)
        {
        }
        LOG_WARNING("EraaseSector: finish Fapi_checkFsmForReady");

        if (retCheck != Fapi_Status_Success)
        {
            // Check Flash API documentation for possible errors
            return 1;
        }
        LOG_WARNING("EraaseSector: pass Fapi_issueAsyncCommandWithAddress");

        // Read FMSTAT register contents to know the status of FSM after
        // erase command to see if there are any erase operation related errors
        if (Fapi_getFsmStatus() != 0)
        {
            // Check Flash API documentation for FMSTAT and debug accordingly
            // Fapi_getFsmStatus() function gives the FMSTAT register contents.
            // Check to see if any of the EV bit, ESUSP bit, CSTAT bit or
            // VOLTSTAT bit is set (Refer to API documentation for more details).
            return 1;
        }
        LOG_WARNING("EraaseSector: pass status");

        // Do blank check
        // Verify sector is erased. The Erase command itself does a verify as
        // it goes.  Hence erase verify by CPU reads (Fapi_doBlankCheck()) is optional.
        retCheck = Fapi_doBlankCheck((unsigned int *)adr, SECTOR_SIZE / 4, &oFlashStatusWord);
        LOG_WARNING("EraaseSector: finish Fapi_doBlankCheck");

        if (retCheck != Fapi_Status_Success)
        {
            // Check Flash API documentation for error info
            return 1;
        }
        LOG_WARNING("EraaseSector: pass Fapi_doBlankCheck");
    }

    return (0); // Finished without Errors
}

static int g32x_erase(struct flash_bank *bank, unsigned int first,
                      unsigned int last)
{
    struct target *target = bank->target;

    LOG_WARNING("enter into g32x_erase function");

    if (g32x_is_otp(bank))
    {
        LOG_ERROR("Cannot erase OTP memory");
        return ERROR_FAIL;
    }

    Fapi_StatusType retCheck;
    Fapi_FlashStateCommandsType oFlashStatusWord;

    // assert((first <= last) && (last < bank->num_sectors));
    assert((first <= last));
    assert((last < bank->num_sectors));

    if (bank->target->state != TARGET_HALTED)
    {
        LOG_ERROR("Target not halted");
        return ERROR_TARGET_NOT_HALTED;
    }
    LOG_WARNING("g32x_erase: finish halt");

    Init(G32R5_FLASH_BANK_BASE, 0, 0);
    LOG_WARNING("g32x_erase: finish init");

    unsigned int addr = 0;
    int retval = 0;

    for (unsigned int i = first; i <= last; i++)
    {
        addr = G32R5_SECTOR_START + (i * G32R5_SECTOR_SIZE);
        retval = EraseSector(addr);
        if (retval != ERROR_OK)
            return retval;
    }
    LOG_WARNING("g32x_erase: finish erase");

    return ERROR_OK;
}

static int g32x_protect(struct flash_bank *bank, int set, unsigned int first,
                        unsigned int last)
{
    return ERROR_OK;
}

static int g32x_probe(struct flash_bank *bank)
{
    struct target *target = bank->target;
    struct g32x_flash_bank *g32x_info = bank->driver_priv;

    uint32_t base_address = 0x08000000;
    int page_size = 8192;
    int num_pages = 80;

    g32x_info->ppage_size = 8;
    g32x_info->probed = false;
    bank->base = base_address;
    bank->size = num_pages * page_size;
    bank->num_sectors = num_pages;
    bank->sectors = alloc_block_array(0, page_size, num_pages);
    if (!bank->sectors)
        return ERROR_FAIL;
    g32x_info->probed = true;

    return ERROR_OK;
}

static int g32x_auto_probe(struct flash_bank *bank)
{
    struct g32x_flash_bank *g32x_info = bank->driver_priv;
    if (g32x_info->probed)
        return ERROR_OK;
    return g32x_probe(bank);
}

static int g32x_protect_check(struct flash_bank *bank)
{
    return ERROR_OK;
}

static int get_g32x_info(struct flash_bank *bank, struct command_invocation *cmd)
{
    unsigned short device_id = 0x001;
    unsigned short rev_id = 0x0000;
    const char *device_str;
    const char *rev_str = NULL;

    command_print(CMD, "enter into get_g32x_info function");

    switch (device_id)
    {
    case 0x001:
        device_str = "G32R501";

        switch (rev_id)
        {
        case 0x0000:
            rev_str = "A";
            break;
        }
        break;

    default:
        command_print_sameline(cmd, "Cannot identify target as a G32R5xx\n");
        return ERROR_FAIL;
    }

    if (rev_str)
        command_print_sameline(cmd, "%s - Rev: %s", device_str, rev_str);
    else
        command_print_sameline(cmd, "%s - Rev: unknown (0x%04x)", device_str, rev_id);

    return ERROR_OK;
}

static const struct command_registration g32r5xx_exec_command_handlers[] = {

    COMMAND_REGISTRATION_DONE};

static const struct command_registration g32r5xx_command_handlers[] = {
    {
        .name = "g32r5xx",
        .mode = COMMAND_ANY,
        .help = "g32r5xx flash command group",
        .usage = "",
        .chain = g32r5xx_exec_command_handlers,
    },
    COMMAND_REGISTRATION_DONE};

const struct flash_driver g32r5xx_flash = {
    .name = "g32r5xx",
    .commands = g32r5xx_command_handlers,
    .flash_bank_command = g32x_flash_bank_command,
    .erase = g32x_erase,
    .protect = g32x_protect,
    .write = g32x_write,
    .read = default_flash_read,
    .probe = g32x_probe,
    .auto_probe = g32x_auto_probe,
    .erase_check = default_flash_blank_check,
    .protect_check = g32x_protect_check,
    .info = get_g32x_info,
    .free_driver_priv = default_flash_free_driver_priv,
};
